//////////////////////////////////////////////////////////////////////////
//
// This file is a part of XORT project.
//
// PROJECT:         Xort compiler
// FILE:            codegen.cpp
// AUTHOR:          Vladimir Gumenuk
// DATE:            27-Mar-2008
// MODIFICATIONS:
//
//
//     Copyright(c) 2014, Vladimir Gumenuk
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification, 
//     are permitted provided that the following conditions are met :
//
//     1. Redistributions of source code must retain the above copyright notice, 
//        this list of conditions and the following disclaimer.
//     2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
//        the following disclaimer in the documentation and / or other materials provided with the distribution.
//     3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 
//        or promote products derived from this software without specific prior written permission.
//
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
//     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
//     THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
//     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
//     OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
//     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
//     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////


#include "xort.h"

STATIC word g_wLabel = 0;
STATIC BYTE_ARRAY g_Code; // This is small optimization of the compilation 
                          // to prevent small memory allocations all the time: 
                          // it will be allocated once and deleted in the end.
void codegen_Init()
{
    g_Code.reserve( 100 ); // allocated enough space for storing 1 instruction :)
}

void codegen_Uninit()
{
    g_Code.clear();
}

word get_NextLabel()
{
    ++g_wLabel;
    return g_wLabel;
}

word get_CurLabel()
{
    return g_wLabel;
}

// called for each generated instruction
STATIC int SaveInstruction( SymbolRef Func, BYTEARRAY Code )
{
    Token Tok = lex_GetLastToken();

    InstructionData PTR instrData NEW_OBJECT(InstructionData); // 0 refs

    int iRet = decode_instr( Code, 0, DEREF(instrData) ); // this also verify instruction and adds textual mnemonic to it.
    if( iRet != XORT_OK ) {
        x_error( ERR_INTERNAL_DECODE_INSTR, Tok.m_dwLine, Tok.m_dwPos );
        message( "SaveInstruction() -> decode instruction failed!" );
        return iRet;
    }
    
    MEMBER_PTR(instrData)m_dwLineNumber = Tok.m_dwLine; // store line number associated with this instruction

    Func.addInstruction( instrData ); // + ref
    Func.m_Value.Clear(); // BUG FIX: since the value is assigned only in gen_loadConst, it must be systematically cleaned up to prevent any ambiguity.  

    Code.zero();

    return XORT_OK;
}

int gen_load( SymbolRef Func, SymbolRef Src )
{
    byte bTypeAddr;
    short wIdx;

    if( Src.m_dwType > t_void )
    {
        if( Src.m_dwType > t_p ) { // all objects are actually refs
            bTypeAddr = t_p;
        } else {
            bTypeAddr = Src.m_dwType;
        }
    } else {
        return ERR_INVALID_TYPE;
    }

    dword dwSize = x_gettypesize( bTypeAddr );
    if( dwSize == 0 ) {
        return ERR_INVALID_TYPE;
    }

    g_Code.storeByte( p_load );

    if( Src.m_SymKind == sk_arg )
    {
        bTypeAddr |= a_s;
        g_Code.storeByte( bTypeAddr );
        wIdx = x_loword( Src.m_iIndex );
        g_Code.storeWord( wIdx );
    }
    else if( Src.m_SymKind == sk_var )
    {
        if( Src.m_dwStorage == cSTORAGE_STACK ) {
            bTypeAddr |= a_s;
            g_Code.storeByte( bTypeAddr );
            wIdx = x_loword( Src.m_iIndex );
            g_Code.storeWord( wIdx );
        } else if( Src.m_dwStorage == cSTORAGE_DATA ) {
            bTypeAddr |= a_m;
            g_Code.storeByte( bTypeAddr );
            g_Code.storeDword( Src.m_iIndex );
        } else if( Src.m_dwStorage == cSTORAGE_MEMBER ) {
            bTypeAddr |= a_ti;
            g_Code.storeByte( bTypeAddr );
            g_Code.storeWord( Src.m_iIndex );
        } else {
            return ERR_INTERNAL;
        }
    }
    else if( Src.m_SymKind == sk_integer )
    {
        bTypeAddr |= a_c;
        g_Code.storeByte( bTypeAddr );

        if( dwSize == 1 ) {
            g_Code.add( Src.m_Value._u1 );
        } else if( dwSize == 2 ) {
            g_Code.storeWord( Src.m_Value._u2 );
        } else if( dwSize == 4 ) {
            if( bTypeAddr & t_r4 ) {
                g_Code.storeFloat( Src.m_Value._r4 );
            } else {
                g_Code.storeDword( Src.m_Value._u4 );
            }
        } else if( dwSize == 8 ) {
            if( bTypeAddr & t_r8 ) {
                g_Code.storeDouble( Src.m_Value._r8 );
            } else {
                g_Code.storeQword( Src.m_Value._u8 );
            }
        } else {
            return ERR_INTERNAL;
        }
    }
    else if( Src.m_SymKind == sk_nullptr )
    {
        bTypeAddr |= a_c;
        g_Code.storeByte( bTypeAddr );
        g_Code.storePointer( Src.m_Value._p );
    } else {
        return ERR_INTERNAL;
    }

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_loadZero( SymbolRef Func, dword dwType, int bCreateObject )
{
    if( dwType == t_void ) {
        return ERR_INVALID_TYPE;
    }

    if( dwType >= t_p )
    {
        if( bCreateObject ) {
            g_Code.storeByte( p_new );
            g_Code.storeByte( t_u4|a_c );
            g_Code.storeDword( dwType );
        } else {
            g_Code.storeByte( p_load );
            g_Code.storeByte( t_p|a_c ); 
            g_Code.storePointer( NULL );
        }
    }
    else
    {
        byte bType = dwType;
        dword dwSize = x_gettypesize( bType );
        if( dwSize == 0 ) {
            return ERR_INVALID_TYPE;
        }

        g_Code.storeByte( p_load );
        g_Code.storeByte( a_c|bType );

        while( dwSize ) {
            g_Code.add( 0 );
            --dwSize;
        }
    }
   
    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_loadConstVar( SymbolRef Func, SymbolRef Src )
{
    if( !( Src.m_dwFlags & f_const ) ) {
        Token Tok = lex_GetLastToken();
        x_error( ERR_INTERNAL_COMPILER, Tok.m_dwLine, Tok.m_dwPos );
        message( "gen_loadConstVar() -> non-constant variable passed" );
        return ERR_INTERNAL;
    }

    if( Src.m_SymKind == sk_var )
    {
        if( Src.m_dwType == t_string ) {
            g_Code.storeByte( p_load );
            g_Code.storeByte( t_p | a_m );
            g_Code.storeDword( Src.m_iIndex );
        } else { // TODO: support other objects!
            Token Tok = lex_GetLastToken();
            x_error( ERR_INTERNAL_COMPILER, Tok.m_dwLine, Tok.m_dwPos );
            message( "gen_loadConstVar() -> unsupported variable type" );
            return ERR_INTERNAL;
        }
    } else {
        Token Tok = lex_GetLastToken();
        x_error( ERR_INTERNAL_COMPILER, Tok.m_dwLine, Tok.m_dwPos );
        message( "gen_loadConstVar() -> argument is not a variable" );
        return ERR_INTERNAL;
    }

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_loadConst( SymbolRef Func, XELEMENT Val )
{
    if( Val.bType >= t_p ) // this should be handled on the upper level
        return ERR_INTERNAL;

    byte bTypeAddr = Val.bType | a_c;

    g_Code.storeByte( p_load );
    g_Code.storeByte( bTypeAddr );

    if( Val.bType == t_s1 || Val.bType == t_u1 ) {
        g_Code.storeByte( Val._u1 );
    } else if( Val.bType == t_s2 || Val.bType == t_u2 ) {
        g_Code.storeWord( Val._u2 );
    } else if( Val.bType == t_s4 || Val.bType == t_u4 ) {
        g_Code.storeDword( Val._u4 );
    } else if( Val.bType == t_s8 || Val.bType == t_u8 ) {
        g_Code.storeQword( Val._u8 );
    } else if( Val.bType == t_r4 ) {
        g_Code.storeFloat( Val._r4 );
    } else if( Val.bType == t_r8 ) {
        g_Code.storeDouble( Val._r8 );
    } else {
        return ERR_INVALID_TYPE;
    }
    
    SaveInstruction( Func, g_Code );
    Func.m_Value.SetValue( Val );
    return XORT_OK;
}

int gen_convert( SymbolRef Func, SymbolRef Dst, SymbolRef Src )
{
    if( Src.m_dwType == Dst.m_dwType ) // no conversion is needed
        return XORT_OK;
    
    if( Src.m_dwType >= t_p && Dst.m_dwType >= t_p ) // in case of different object types this should be handled at parser level
        return XORT_OK;

    if( ( Src.m_dwType >= t_p && Src.m_SymKind == sk_nullptr ) && ( Dst.m_dwType >= t_p ) )
    {
        return XORT_OK; // no conversion needed in this case
    }

    if( Src.m_dwType >= t_p || Src.m_dwType == t_void || Dst.m_dwType >= t_p || Dst.m_dwType == t_void )
    {
        Token Tok = lex_GetLastToken();
        x_error( ERR_IMPOSSIBLE_CONVERSION, Tok.m_dwLine, Tok.m_dwPos );
        return ERR_CONVERSION_NOTPOSSIBLE;
    }

    //////////////////////////////////////////////////////////////////////////
    // This is the example of a typical case that requires peep-hole optimization:
    // 33 - [ip:117]: load.c.s4    1
    // 34 - [ip:123]: convert.s4u4
    // This can be simply replaced by single instruction: load.c.u4 1
    //
    InstructionData PTR lastInstr = Func.getLastInstruction();
    byte bLastTypeAddr = MEMBER_PTR(lastInstr)m_bTypeAddr;
    byte bLastType = bLastTypeAddr & 0xF0;
    if( lastInstr && GetCompilationFlags() & XCL_OPTIMIZATION_ON && MEMBER_PTR(lastInstr)m_bInstrCode == p_load && 
        bLastTypeAddr & a_c && bLastType < t_p && Src.m_dwType == Func.m_Value.bType ) // check for possible optimization
    {
        if( Func.m_Value.bType == t_v || Func.m_Value.bType == t_p ) {
            Token Tok = lex_GetLastToken();
            x_error( ERR_INTERNAL_COMPILER, Tok.m_dwLine, Tok.m_dwPos );
            return ERR_INTERNAL;
        }

        XortElement targetElem;
        int iRet = x_convert_type( Dst.m_dwType, targetElem, Src.m_Value ); // try to convert to target type
        if( iRet != XORT_OK ) {
            Token Tok = lex_GetLastToken();
            x_error( iRet, Tok.m_dwLine, Tok.m_dwPos );
            return iRet;
        }

        Func.removeLastInstr();

        iRet = gen_loadConst( Func, targetElem );
        if( iRet != XORT_OK ) {
            Token Tok = lex_GetLastToken();
            x_error( iRet, Tok.m_dwLine, Tok.m_dwPos );
            return iRet;
        }
    }
    else // else, generate normal types conversion.
    {
        byte bTypeAddr = Dst.m_dwType;
        bTypeAddr |= ( Src.m_dwType >> 4 );

        g_Code.storeByte( p_convert );
        g_Code.storeByte( bTypeAddr );

        SaveInstruction( Func, g_Code );
    }

    Src.m_dwType = Dst.m_dwType; // change source type after conversion!

    return XORT_OK;
}

int gen_neg( SymbolRef Func, SymbolRef Dst )
{
    // this is a very strict condition :)
    if( Dst.m_dwType != t_s1 && Dst.m_dwType != t_s2 && Dst.m_dwType != t_s4 && Dst.m_dwType != t_s8 &&
        Dst.m_dwType != t_r4 && Dst.m_dwType != t_r8 ) {
        Token Tok = lex_GetLastToken();
        x_error( ERR_UNARY_MINUS_NOT_ALLOWED, Tok.m_dwLine, Tok.m_dwPos );
        return ERR_CONVERSION_NOTPOSSIBLE;
    }

    byte bTypeAddr = Dst.m_dwType;

    g_Code.storeByte( p_neg );
    g_Code.storeByte( bTypeAddr );

    SaveInstruction( Func, g_Code );
    return XORT_OK;   
}

int gen_not( SymbolRef Func, SymbolRef Dst )
{
    if( Dst.m_dwType == t_void || Dst.m_dwType == t_r4 || Dst.m_dwType == t_r8 || Dst.m_dwType >= t_p ) {
        Token Tok = lex_GetLastToken();
        x_error( ERR_UNARY_NOT_NOT_ALLOWED, Tok.m_dwLine, Tok.m_dwPos );
        return ERR_CONVERSION_NOTPOSSIBLE;
    }

    byte bTypeAddr = Dst.m_dwType;

    g_Code.storeByte( p_not );
    g_Code.storeByte( bTypeAddr );

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_lognot( SymbolRef Func, SymbolRef Dst )
{
    if( Dst.m_dwType == t_void || Dst.m_dwType == t_r4 || Dst.m_dwType == t_r8 ) {
        Token Tok = lex_GetLastToken();
        x_error( ERR_UNARY_LOGNOT_NOT_ALLOWED, Tok.m_dwLine, Tok.m_dwPos );
        return ERR_IMPOSSIBLE_CONVERSION;
    }

    byte bTypeAddr;
    if( Dst.m_dwType >= t_p ) {
        bTypeAddr = t_p;
    } else {
        bTypeAddr = Dst.m_dwType;
    }

    g_Code.storeByte( p_lnot );
    g_Code.storeByte( bTypeAddr );

    Dst.m_SymKind = sk_stackvar; // the result is logical!!!
    Dst.m_dwType = t_s4;
    Dst.m_dwStorage = cSTORAGE_STACK;

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_arith( byte bCode, SymbolRef Func, SymbolRef Dst, SymbolRef Src )
{
    int iRet;

    if( Dst.m_dwType != Src.m_dwType ) {
        iRet = gen_convert( Func, Dst, Src );
        if( iRet != XORT_OK ) {
            return iRet;
        }
    }

    byte bTypeAddr;

    if( Dst.m_dwType > t_void && Dst.m_dwType < t_p ) {
        bTypeAddr = Dst.m_dwType;
    } else {
        if( ( Src.m_dwType == t_p && Src.m_SymKind == sk_nullptr ) && ( Dst.m_dwType >= t_p ) )
        {
            if( bCode == p_cmpeq || bCode == p_cmpneq )
                bTypeAddr = t_p;
            else
                return ERR_INVALID_TYPE; // pointer arithmetics is not supported!
        } else {
            return ERR_INVALID_TYPE; // pointer arithmetics is not supported!
        }
    }

    if( GetCompilationFlags() & XCL_OPTIMIZATION_ON ) // this will optimize the following instrucitons sequence
    {   /*
        000006 - [ip:000005] : load.c.s4    48 - loading 2 constants and arith between them can be replaced by final arith result
        000007 - [ip:000006] : load.c.s4    1
        000008 - [ip:000007] : or.s4
        */
        InstructionData PTR lastInstr = Func.getLastInstruction();
        if( lastInstr && MEMBER_PTR( lastInstr )m_bInstrCode == p_load )
        {
            byte bType = MEMBER_PTR( lastInstr )m_bTypeAddr & 0xF0;
            byte bAddr = MEMBER_PTR( lastInstr )m_bTypeAddr & 0x0F;

            if( bAddr == a_c && bType > t_v && bType < t_p )
            {
                InstructionData PTR lastInstr2 = Func.getInstructionByIndex( Func.getInstrCount() - 2 );
                if( lastInstr2 && MEMBER_PTR( lastInstr2 )m_bInstrCode == p_load )
                {
                    byte bType2 = MEMBER_PTR( lastInstr2 )m_bTypeAddr & 0xF0;
                    byte bAddr2 = MEMBER_PTR( lastInstr2 )m_bTypeAddr & 0x0F;

                    if( bAddr2 == a_c && bType2 > t_v && bType2 < t_p && bType == bType2 )
                    {
                        XortElement Value;
                        Value.SetValue( MEMBER_PTR( lastInstr2 )m_Value );

                        dword dwOp = x_tok_from_code( bCode );
                        if( dwOp ) // 0 indicates that this op is no possible...
                        {
                            iRet = const_arith( dwOp, Value, MEMBER_PTR( lastInstr )m_Value );
                            if( iRet == XORT_OK )
                            {
                                Func.removeLastInstr(); // remove last genereted p_load instruction

                                MEMBER_PTR( lastInstr2 )m_Value.SetValue( Value ); // modify the value of previous load instruction to the constant result
                                iRet = decode_instr_text( DEREF( lastInstr2 ) );
                                if( iRet != XORT_OK )
                                    return ERR_INTERNAL_COMPILER;

                                Dst.m_Value.SetValue( Value ); // also copy final result to destination operand, to enable it in analyzer.
                                return XORT_OK;
                            } // else we just generate the arith operation code
                        }
                    }
                }
            }
        }
    }

    g_Code.storeByte( bCode );
    g_Code.storeByte( bTypeAddr );

    SaveInstruction( Func, g_Code );
    return XORT_OK;   
}

int gen_cmp( byte bCode, SymbolRef Func, SymbolRef Dst, SymbolRef Src )
{
    byte bTypeAddr;
    int iRet;

    if( Dst.m_dwType != Src.m_dwType ) {
        iRet = gen_convert( Func, Dst, Src );
        if( iRet != XORT_OK ) {
            return iRet;
        }
    }

    if( Dst.m_dwType > t_void && Dst.m_dwType < t_p ) {
        bTypeAddr = Dst.m_dwType;
    }
    else
    {
        if( ( Src.m_dwType == t_p && Src.m_SymKind == sk_nullptr ) && ( Dst.m_dwType >= t_p ) )
        {
            if( bCode == p_cmpeq || bCode == p_cmpneq )
                bTypeAddr = t_p;
            else
                return ERR_INVALID_TYPE; // pointer arithmetics is not supported!
        } else {
            return ERR_INVALID_TYPE; // pointer arithmetics is not supported!
        }
    }

    g_Code.storeByte( bCode );
    g_Code.storeByte( bTypeAddr );

    Dst.m_SymKind   = sk_stackvar;
    Dst.m_dwType    = t_s4;
    Dst.m_dwStorage = cSTORAGE_STACK;
    
    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

// Logical arithmetic is used for || (logical OR) and && (logical AND) operations.
// The VM checks arguments for non-zero values and computes result accordingly.
// The result will be integer s4 value: 0 or 1
int gen_logic_arith( byte bCode, SymbolRef Func, SymbolRef Dst, SymbolRef Src, SymbolRef Result )
{
    if( bCode != p_lor && bCode != p_land )
        return ERR_INTERNAL;

    if( Dst.m_dwType == t_r4 || Dst.m_dwType == t_r8 || Src.m_dwType == t_r4 || Src.m_dwType == t_r8 )
        return ERR_INVALID_TYPE;
    
    byte bTypeAddr  = ( Dst.m_dwType > t_p ) ? t_p : Dst.m_dwType;
    byte bType2     = ( Src.m_dwType > t_p ) ? t_p : Src.m_dwType;
    bTypeAddr      |= ( bType2 >> 4 );

    g_Code.storeByte( bCode );
    g_Code.storeByte( bTypeAddr );

    Result.m_SymKind = sk_stackvar;
    Result.m_dwType = t_s4;
    Result.m_dwStorage = cSTORAGE_STACK;

    SaveInstruction( Func, g_Code );
    return XORT_OK;   
}

int gen_assign( SymbolRef Func, SymbolRef Dst, SymbolRef Src )
{
    if( Dst.m_dwType != Src.m_dwType )
    {
        if( Dst.m_dwType >= t_p && Src.m_SymKind == sk_nullptr )
        {
            // this let to assign NULL to objects
        } else {
            int iRet = gen_convert( Func, Dst, Src );
            if( iRet != XORT_OK ) {
                return iRet;
            }
        }
    }

    if( Dst.m_dwType == t_void ) {
        return ERR_INVALID_TYPE;
    }

    byte bTypeAddr;
    short wIdx;

    if( Dst.m_dwType > t_p ) { // all objects are actually refs
        bTypeAddr = t_p;
    } else {
        bTypeAddr = Dst.m_dwType;
    }

    g_Code.storeByte( p_store );

    if( Dst.m_SymKind == sk_arg )
    {
        bTypeAddr |= a_s;
        g_Code.storeByte( bTypeAddr );
        wIdx = x_loword( Dst.m_iIndex );
        g_Code.storeWord( wIdx );
    }
    else if( Dst.m_SymKind == sk_var )
    {
        if( Dst.m_dwStorage == cSTORAGE_STACK )
        {
            bTypeAddr |= a_s;
            g_Code.storeByte( bTypeAddr );
            wIdx = x_loword( Dst.m_iIndex );
            g_Code.storeWord( wIdx );
        }
        else if( Dst.m_dwStorage == cSTORAGE_DATA )
        {
            bTypeAddr |= a_m;
            g_Code.storeByte( bTypeAddr );
            g_Code.storeDword( Dst.m_iIndex );
        } 
        else if( Dst.m_dwStorage == cSTORAGE_MEMBER )
        {
            bTypeAddr |= a_ti;
            g_Code.storeByte( bTypeAddr );
            g_Code.storeWord( Dst.m_iIndex );
        } else {
            return ERR_INTERNAL;
        }
    } else {
        return ERR_INTERNAL;
    }

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_ret( SymbolRef Func, SymbolRef Dst, SymbolRef Src )
{
    if( GetCompilationFlags() & XCL_OPTIMIZATION_ON ) {
        // OPTIMIZATION: remove enter instruction if current is ret.v
        //000055-[ip:000203]:        ifz.s4    __@L10                
        //000056-[ip:000207]:        enter                <<<--- UNNECECARY INSTRUCTION
        //000057-[ip:000209]:        ret.v
        //000057-[ip:000210]:        leave                <<<--- UNNECECARY INSTRUCTION
        InstructionData PTR lastInstr = Func.getLastInstruction();
        if( lastInstr && MEMBER_PTR(lastInstr)m_bInstrCode == p_enter ) {
            Func.removeLastInstr();
        }
    }

    byte bTypeAddr;

    if( Dst.m_dwType != Src.m_dwType )
    {
        int iRet = gen_convert( Func, Dst, Src );
        if( iRet != XORT_OK ) {
            return iRet;
        }
    }

    if( Dst.m_dwType > t_p ) { // all objects are actually refs
        bTypeAddr = t_p;
    } else {
        bTypeAddr = Dst.m_dwType;
    }

    g_Code.storeByte( p_ret );
    g_Code.storeByte( bTypeAddr );

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_ret2( SymbolRef Func, SymbolRef Dst )
{
    if( GetCompilationFlags() & XCL_OPTIMIZATION_ON ) {
        InstructionData PTR lastInstr = Func.getLastInstruction();
        if( lastInstr && MEMBER_PTR(lastInstr)m_bInstrCode == p_enter ) { // OPTIMIZATION: remove enter instruction if current is ret.v
            Func.removeLastInstr();
        }
    }

    byte bTypeAddr;

    if( Dst.m_dwType > t_p ) { // all objects are actually refs
        bTypeAddr = t_p;
    } else {
        bTypeAddr = Dst.m_dwType;
    }

    g_Code.storeByte( p_ret );
    g_Code.storeByte( bTypeAddr );

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_call( SymbolRef Func, SymbolRef ProcToCall )
{
    byte bTypeAddr;
    if( ProcToCall.m_dwType > t_p ) { // all objects are actually refs
        bTypeAddr = t_p;
    } else {
        bTypeAddr = ProcToCall.m_dwType;
    }

    g_Code.storeByte( p_calli );
    g_Code.storeByte( bTypeAddr );
    g_Code.storeDword( ProcToCall.m_iIndex );

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_dup( SymbolRef Func )
{
    g_Code.storeByte( p_dup );
    g_Code.storeByte( 0 );
    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_drop( SymbolRef Func )
{
    g_Code.storeByte( p_drop );
    g_Code.storeByte( t_v ); // 0
    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

STATIC int gen_if( byte bCode, SymbolRef Func, SymbolRef Dst, word wLblIdx )
{
    byte bTypeAddr;

    if( Dst.m_dwType >= t_p ) {
        bTypeAddr = t_p;
    } else {
        bTypeAddr = Dst.m_dwType;
    }

    g_Code.storeByte( bCode );
    g_Code.storeByte( bTypeAddr );
    g_Code.storeWord( wLblIdx ); // reserve space for address

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_ifnz( SymbolRef Func, SymbolRef Dst, word wLblIdx )
{
    return gen_if( p_ifnz, Func, Dst, wLblIdx );
}

int gen_ifz( SymbolRef Func, SymbolRef Dst, word wLblIdx )
{
    return gen_if( p_ifz, Func, Dst, wLblIdx );
}

int gen_ifneq( SymbolRef Func, SymbolRef Dst, SymbolRef Src, word wLblIdx )
{
    if( Dst.m_dwType != Src.m_dwType )
    {
        int iRet = gen_convert( Func, Dst, Src );
        if( iRet != XORT_OK ) {
            return iRet;
        }
    }

    if( Dst.m_dwType == t_void ) {
        return ERR_INVALID_TYPE;
    }

    return gen_if( p_ifneq, Func, Dst, wLblIdx );
}

int gen_label( SymbolRef Func, word wLblIdx )
{
    g_Code.storeByte( p_label );
    g_Code.storeWord( wLblIdx );
    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_goto( SymbolRef Func, word wLblIdx )
{
    g_Code.storeByte( p_goto );
    g_Code.storeByte( t_s2|a_c );
    g_Code.storeWord( wLblIdx );
    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_enter( SymbolRef Func )
{
    g_Code.storeByte( p_enter );
    g_Code.storeByte( 0 );
    pushStackVarIdx();
    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_leave( SymbolRef Func, int bDecStkIdx )
{
    if( bDecStkIdx ) {
        popStackVarIdx();
    }

    if( GetCompilationFlags() & XCL_OPTIMIZATION_ON ) {
        InstructionData PTR lastInstr = Func.getLastInstruction();
        if( MEMBER_PTR(lastInstr)m_bInstrCode == p_enter ) // OPTIMIZATION : remove stupid enter/leave instruction pairs
        {
            Func.removeLastInstr();
        }
        else
        // OPTIMIZATION: this condition can be totally removed for code clarity!
        /* Example of generated code without this condition:
        000057-[ip:000207]:        ifz.s4    __@L10                
        000058-[ip:000211]:        enter                                                            <<<--- UNNECECARY INSTRUCTION
        000059-[ip:000213]:        leave                               ;[36]             continue;  <<<--- UNNECECARY INSTRUCTION
        000060-[ip:000215]:        leave                           
        000061-[ip:000217]:        goto.c.s2    __@L7              
        000062-[ip:000221]:        leave                               ;[37]         } <<<--- UNNECECARY INSTRUCTION
        -----------------------------------------------------------------------------------------------
        ...and the same code with:
        000055-[ip:000203]:        ifz.s4    __@L10                
        000056-[ip:000207]:        enter                           
        000057-[ip:000209]:        leave                               ;[36]             continue;
        000058-[ip:000211]:        leave                           
        000059-[ip:000213]:        goto.c.s2    __@L7              
        */
        if( MEMBER_PTR(lastInstr)m_bInstrCode != p_goto && MEMBER_PTR(lastInstr)m_bInstrCode != p_ret ) {
            g_Code.storeByte( p_leave );
            g_Code.storeByte( 0 );
            SaveInstruction( Func, g_Code );
        }
    } else {
        g_Code.storeByte( p_leave );
        g_Code.storeByte( 0 );
        SaveInstruction( Func, g_Code );
    }
        
    return XORT_OK;
}

int gen_member_get( SymbolRef Func, SymbolRef Object, SymbolRef Member )
{
    byte bTypeAddr;

    if( Member.m_SymKind == sk_proc )
    {
        return ERR_INTERNAL;

    } else if( Member.m_SymKind == sk_var ) {

        if( Member.m_dwType > t_p ) {
            bTypeAddr = t_p;
        } else {
            bTypeAddr = Member.m_dwType;
        }

        g_Code.storeByte( p_get );
        g_Code.storeByte( bTypeAddr );

        return SaveInstruction( Func, g_Code );
    } else {
        return ERR_INTERNAL;
    }

    return ERR_INTERNAL;
}

int gen_member_put( SymbolRef Func, SymbolRef Member, SymbolRef Src )
{
    byte bTypeAddr;

    if( Member.m_SymKind == sk_proc )
    {
        return ERR_INTERNAL;

    } else if( Member.m_SymKind == sk_var ) {

        if( Member.m_dwType > t_p ) {
            bTypeAddr = t_p;
        } else {
            bTypeAddr = Member.m_dwType;
        }

        g_Code.storeByte( p_put );
        g_Code.storeByte( bTypeAddr );

        return SaveInstruction( Func, g_Code );
    } else {
        return ERR_INTERNAL;
    }
        
    return ERR_INTERNAL;
}

int gen_invoke( SymbolRef Func, SymbolRef ProcToCall )
{
    byte bTypeAddr;

    if( ProcToCall.m_dwType > t_p ) {
        bTypeAddr = t_p;
    } else {
        bTypeAddr = ProcToCall.m_dwType;
    }

    g_Code.storeByte( p_invoke );  
    g_Code.storeByte( bTypeAddr );

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_invoke_ti( SymbolRef Func, SymbolRef ProcToCall )
{
    byte bTypeAddr;

    if( ProcToCall.m_dwType > t_p ) {
        bTypeAddr = t_p;
    } else {
        bTypeAddr = ProcToCall.m_dwType;
    }

    bTypeAddr |= a_ti;

    g_Code.storeByte( p_invoke );  
    g_Code.storeByte( bTypeAddr );
    g_Code.storeWord( ProcToCall.m_iIndex );

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_inc_dec( byte bCode, SymbolRef Func, SymbolRef Target )
{
    if( bCode != p_inc && bCode != p_dec ) {
        return ERR_INTERNAL;
    }
    if( Target.m_dwType == t_void ) { //|| Target.m_dwType == t_r4 || Target.m_dwType == t_r8 || Target.m_dwType >= t_p ) {
        return ERR_INTERNAL;
    }

    word wIdx;
    byte bTypeAddr = Target.m_dwType;

    g_Code.storeByte( bCode );

    if( Target.m_SymKind == sk_arg )
    {
        bTypeAddr |= a_s;
        g_Code.storeByte( bTypeAddr );
        wIdx = x_loword( Target.m_iIndex );
        g_Code.storeWord( wIdx );
    }
    else if( Target.m_SymKind == sk_var )
    {
        if( Target.m_dwStorage == cSTORAGE_STACK )
        {
            bTypeAddr |= a_s;
            g_Code.storeByte( bTypeAddr );
            wIdx = x_loword( Target.m_iIndex );
            g_Code.storeWord( wIdx );
        }
        else if( Target.m_dwStorage == cSTORAGE_DATA )
        {
            bTypeAddr |= a_m;
            g_Code.storeByte( bTypeAddr );
            g_Code.storeDword( Target.m_iIndex );
        }
        else if( Target.m_dwStorage == cSTORAGE_MEMBER )
        {
            bTypeAddr |= a_ti;
            g_Code.storeByte( bTypeAddr );
            g_Code.storeWord( Target.m_iIndex );
        } else {
            return ERR_INTERNAL;
        }
    } else {
        return ERR_INTERNAL;
    }

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_inc_dec_onStackTop( byte bCode, SymbolRef Func, SymbolRef Target )
{
    if( bCode != p_inc && bCode != p_dec ) {
        return ERR_INTERNAL;
    }
    if( Target.m_dwType == t_void ) { //|| Target.m_dwType == t_r4 || Target.m_dwType == t_r8 || Target.m_dwType >= t_p ) {
        return ERR_INTERNAL;
    }

    byte bTypeAddr = ( Target.m_dwType > t_p ) ? t_p : Target.m_dwType; // | a_0 - means at stack top

    g_Code.storeByte( bCode );
    g_Code.storeByte( bTypeAddr );

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_nz( SymbolRef Func, SymbolRef Target )
{
    if( Target.m_dwType == t_void ) { // this works for all types
        return ERR_INTERNAL;
    }

    byte bTypeAddr = ( Target.m_dwType > t_p ) ? t_p : Target.m_dwType;

    g_Code.storeByte( p_nz );
    g_Code.storeByte( bTypeAddr );

    Target.m_SymKind = sk_stackvar;         // the result is logical!!!
    Target.m_dwType = t_s4;                 // s4 type is selected for boolean operations...
    Target.m_dwStorage = cSTORAGE_STACK;

    SaveInstruction( Func, g_Code );
    return XORT_OK;
}

int gen_pack( SymbolRef Func, SymbolRef Target )
{
    int iRet = ERR_INTERNAL;

    if( Target.m_dwType >= t_p && Target.m_dwType <= t_system )
    {
        g_Code.storeByte( p_pack );
        g_Code.storeByte( t_p );
        iRet = XORT_OK;
        SaveInstruction( Func, g_Code );
    }

    return iRet;
}

int gen_unpack( SymbolRef Func, SymbolRef Target )
{
    int iRet = ERR_INTERNAL;

    if( Target.m_dwType >= t_p && Target.m_dwType <= t_system )
    {
        g_Code.storeByte( p_upack );
        g_Code.storeByte( t_p );
        iRet = XORT_OK;
        SaveInstruction( Func, g_Code );
    }

    return iRet;
}

//////////////////////////////////////////////////////////////////////////
// helper meta (pseudo) instructions
STATIC void store_MetaInstruction( byte bInstr, SymbolRef Func, word wIdx )
{
    g_Code.storeByte( bInstr );
    g_Code.storeWord( wIdx );
    SaveInstruction( Func, g_Code );
}

void gen_StackVarMark( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_stkvar, Func, wIdx );
    }
}

void gen_StackVarEnd( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_endstkvar, Func, wIdx );
    }
}

void gen_BeginArg( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_beginarg, Func, wIdx );
    }
}

void gen_EndArg( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_endarg, Func, wIdx );
    }
}

void gen_BeginCall( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_begincall, Func, wIdx );
    }
}

void gen_EndCall( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_endcall, Func, wIdx );
    }
}

void gen_BeginIf( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_begin_if, Func, wIdx );
    }
}

void gen_EndIf( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_end_if, Func, wIdx );
    }
}

void gen_BeginRet( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_beginret, Func, wIdx );
    }
}

void gen_EndRet( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_endret, Func, wIdx );
    }
}

void gen_BeginAssign( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_beginasgn, Func, wIdx );
    }
}

void gen_EndAssign( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_endasgn, Func, wIdx );
    }
}

void gen_BeginSwitch( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_begswitch, Func, wIdx );
    }
}

void gen_EndSwitch( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_endswitch, Func, wIdx );
    }
}

void gen_BeginCase( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_begcase, Func, wIdx );
    }
}

void gen_EndCase( SymbolRef Func, word wIdx )
{
    if( GetCompilationFlags() & XCL_META_INSTRUCTIONS_ON )
    {
        store_MetaInstruction( p_endcase, Func, wIdx );
    }
}

